package fuse.LaCollaFS.Metadades;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

import java.io.IOException;

import java.io.PrintWriter;

import java.nio.ByteBuffer;
import java.rmi.RemoteException;
import java.util.Date;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import fuse.FuseException;
import fuse.FuseStatfs;
import fuse.LaCollaFS.Api.*;
import fuse.LaCollaFS.Metadades.Directori;
import fuse.LaCollaFS.Metadades.Estructura;
import fuse.compat.FuseStat;

import LaColla.Api.Api;

import LaColla.core.data.app.ObjectLaCOLLA;

//import com.sun.org.apache.xml.internal.resolver.helpers.Debug;

//import LaColla.core.util.Debug;

public class MetaDades {

	// fuse variables

	private FuseStatfs statfs;

	private Directori tree;

	private String defpath;

	private String defpathmetadades = "/tmp/lacollafsmetadades";

	private static final int blockSize = 512;

	private Api api;

	private String treeObjId;

	private ApplicationsSideApiServer asas;

	/**
	 * Constructor de les metadades
	 * 
	 * @param ip
	 *            crec que ja no cal
	 * @param a
	 *            enllaç a l'api de la colla
	 * @param as
	 *            enllaç al applications sideapi
	 * @param defaultpath
	 *            path per defecte on guardar el docs en local
	 * @throws RemoteException
	 */
	public MetaDades(String ip, Api a, ApplicationsSideApiServer as,
			String defaultpath) throws RemoteException {
		tree = new Directori("/", this);
		this.api = a;
		int files = 0;
		int dirs = 0;
		int blocks = 0;
		this.asas = as;

		statfs = new FuseStatfs();
		statfs.blocks = blocks;
		statfs.blockSize = blockSize;
		statfs.blocksFree = 0;
		statfs.files = files + dirs;
		statfs.filesFree = 0;
		statfs.namelen = 2048;

		defpath = defaultpath;
		File file = new File(defpathmetadades);
		file.mkdirs();
		p("dir creat!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		this.DesaTree();

	}

	/**
	 * Imprimeix per pantalla
	 * 
	 * @param o
	 */
	private static void p(Object o) {
		System.out.println(o);
	}

	/**
	 * Obte el FuseStat del Objecte path
	 * 
	 * @param path
	 * @return
	 * @throws FuseException
	 */
	public FuseStat ObtenirStat(String path) throws FuseException {

		Estructura node;
		FuseStat stat = null;
		// p("obtenint node..."+path);
		node = ObtenirNode(path);
		if (node == null) {
			throw new FuseException("No existeix el path")
					.initErrno(FuseException.ENOENT);
		} else
			stat = node.getStat();
		// TODO Auto-generated method stub
		return stat;
	}

	/**
	 * Canvia el FuseStat d'un objecte i guarda el arbre de les metadades a
	 * lacolla
	 * 
	 * @param path
	 *            node a canviar l'estat
	 * @param estat
	 *            FuseStat amb els canvis a realitzar
	 */
	public void CanviarStat(String path, FuseStat estat) {

		Estructura node;
		// p("canviar estat de : "+path);
		node = ObtenirNode(path);

		node.setStat(estat);
		DesaTree();

	}

	/**
	 * Obté l'Objecte amb el node demanat.
	 * 
	 * @param path
	 *            path del objecte a recuperar
	 * @return retorna el Objecte en questió
	 */
	public Estructura ObtenirNode(String path) {
		Estructura node;
		try {
			this.ObtenirTree();
		} catch (FileNotFoundException e) {
			p("Error al no trobat el fitxer!!!!");
			// e.printStackTrace();
		} catch (JDOMException e) {
			p("Error amb el jdom!!!!!");
		} catch (IOException e) {
			p("error io!!!!!");
		}
		node = tree.BuscarFill(path);

		return node;
	}

	/**
	 * Obté l'arbre de les metadades del fitxer XML guardat a LaColla
	 * 
	 * @throws FileNotFoundException
	 * @throws JDOMException
	 * @throws IOException
	 */
	public void ObtenirTree() throws FileNotFoundException, JDOMException,
			IOException {

		String groupId = "hola";
		ObjectLaCOLLA obj;
		File file = new File(defpathmetadades + "/dir.xml");
		obj = new ObjectLaCOLLA(null, new Date(), "", groupId, file.length(),
				file.getAbsolutePath(), file.getName());

		treeObjId = ((ApplicationsSideApiImpl) asas.getASAI()).gettreeObjId();
		// p("objecte lacolla a recuperar : "+treeObjId);

		obj.setObjectId(treeObjId);
		// p("objecte passat per l'api de lacolla...");
		// p("obtenir objecte de la colla, get object!!!!"+ obj.getObjectId());

		api.getObject(obj, file.getAbsolutePath());

		// p("objecte aconsegit!!!!!!!!!!!!!!!!!!!");

		// treeObjId = ((ApplicationsSideApiImpl)asas.getASAI()).gettreeObjId();

		SAXBuilder builder2 = new SAXBuilder();
		Document doc2 = builder2.build(new FileInputStream(defpathmetadades
				+ "/dir.xml"));
		Element rootl = doc2.getRootElement();
		// tree = new Directori("", null);
		tree.LlegirXML(rootl);

	}

	/**
	 * Guarda el l'Arbre de les metadades en un fitxer XML i el desa a laCOlla
	 * 
	 */
	@SuppressWarnings("deprecation")
	public void DesaTree() {
		p("-----------------Vaig a desar el arbre de les metadades--------------------");
		Element rootdir = tree.CrearXML();
		// p("xml creat, anem a escriure'l...");
		XMLOutputter outputter2 = new XMLOutputter("", true);

		try {
			outputter2.output(new Document(rootdir), new FileOutputStream(
					defpathmetadades + "/dir.xml"));
		} catch (FileNotFoundException e1) {
			p("Error amb el fitxer!!!! al intentar escriure el xml al disc");
		} catch (IOException e1) {
			p("error amb io al escriure el xml al disc!!!");
			// e1.printStackTrace();
		}

		// api.getInfoGroup(arg0, arg1, arg2); obtenir grupId, mentrestant...
		String groupId = "hola";
		ObjectLaCOLLA obj = null;
		File file = new File(defpathmetadades + "/dir.xml");
		obj = new ObjectLaCOLLA(null, new Date(), "", groupId, file.length(),
				file.getAbsolutePath(), file.getName());
		
		treeObjId = ((ApplicationsSideApiImpl) asas.getASAI()).gettreeObjId();
		p("IDObject abans de fer el put...."+treeObjId);
		obj.setObjectId(treeObjId);
		
		try {
			obj = api.putObject(obj);
			p("IdObject dp de fer el put.... "+ obj.getObjectId());
		} catch (RemoteException e) {

			p("!!!!!!!!!!!!!!error al fer el putobject!!!!!!!!!!!!!!!!!!!11");
			// e.printStackTrace();
		}
		// p("--------------dir.xml ObjecteDipositat---------------------: " +
		// p("id del objecte dir.xml...."+treeObjId);
		treeObjId = ((ApplicationsSideApiImpl) asas.getASAI()).gettreeObjId();

		p("IdObject que em porna l'api de la colla..."+treeObjId);
	}

	/**
	 * Elimina node del arbre de directoris TODO
	 * 
	 * @param path
	 *            Path del arbre a eliminar
	 */
	public void EliminarNode(String path) {

	}

	/**
	 * Retorna el Stat del FileSystem TODO
	 * 
	 * @return
	 */
	public FuseStatfs getStat() {

		return statfs;
	}

	/**
	 * Escriu canvis en un fitxer i el guarda a lacolla Moficia l'estat del
	 * Fitxer en l'estructura a les metadades.
	 * 
	 * TODO REPASAR OFFSET!!!! ara mateix no fa el que hauria de fer!!!!
	 * 
	 * @param path
	 *            path del fitxer a modifcar
	 * @param buf
	 *            dades a afegir al fitxer
	 * @param offset
	 *            offset on començar a escriure en el fitxer
	 */
	public void Escriurefitxer(String path, ByteBuffer buf, long offset) {
		Fitxer fit;
		fit = (Fitxer) this.ObtenirNode(path);

		// buf.position((int) offset);
		File fichero = new File(defpath + path);
		PrintWriter pw;
		try {
			pw = new PrintWriter(fichero);

			for (long i = 0; i < buf.capacity(); i++) {

				if (i < offset) {
					// no se com moure el punter!!!!
				} else {
					pw.write(buf.get((int) i));
				}

			}
			pw.close();
			String groupId = "hola";
			ObjectLaCOLLA obj = new ObjectLaCOLLA(null, new Date(), "",
					groupId, fichero.length(), fichero.getAbsolutePath(),
					fichero.getName());
			obj = api.putObject(obj);
			fit.setIdObjecte(obj.getObjectId());
			// p("el fitxer té el objectid seguent : " + fit.getIdObjecte());
		} catch (FileNotFoundException e) {
			p("error al no trobar un fitxer");
			// e.printStackTrace();
		} catch (RemoteException e) {
			p("error en el put de un fitxer!!!");
			// e.printStackTrace();
		}

		// p("--------------dir.xml ObjecteDipositat---------------------: " +
		// obj.getObjectId());

		FuseStat estat = fit.getStat();
		estat.size = estat.size + buf.capacity();
		estat.blocks = (int) ((estat.size + 511L) / 512L);
		fit.setStat(estat);
		this.DesaTree();

	}

	public ByteBuffer Llegirfitxer(String path, long offset, ByteBuffer buf) {
		Fitxer fit;
		fit = (Fitxer) this.ObtenirNode(path);
		File fichero = new File(defpath + path);

		String groupId = "hola";
		ObjectLaCOLLA obj = new ObjectLaCOLLA(null, new Date(), "", groupId,
				fichero.length(), fichero.getAbsolutePath(), fichero.getName());

		// treeObjId = ((ApplicationsSideApiImpl)asas.getASAI()).gettreeObjId();
		// p("objecte lacolla a recuperar : "+treeObjId);

		obj.setObjectId(fit.getIdObjecte());
		// p("objecte passat per l'api de lacolla...");
		// p("obtenir objecte de la colla, get object!!!!"+ obj.getObjectId());

		try {
			api.getObject(obj, fichero.getAbsolutePath());
			// p("objecte recuperat ? "+ fichero.exists());
			FileInputStream fitxer = new FileInputStream(fichero);
			byte[] buffer = new byte[100];
			while (fitxer.read(buffer) != -1) { // mentre no s'acabi el fitxer,
				// anar llegint
				for (long i = offset; i < buffer.length; i++) { // començant per
					// l'offset,
					// anar passant
					// les dades del
					// buffer de
					// bytes al
					// byteBuffer
					// que hem de
					// retornar
					buf.put(buffer[(int) i]);
				}

			}
			;
			// llegir l'ultima rafega de bytes
			for (long i = offset; i < buffer.length; i++) {
				buf.put(buffer[(int) i]);
			}

		} catch (RemoteException e) {
			p("error remote exception");
			// e.printStackTrace();

		} catch (IOException e) {
			p("error io");
			// e.printStackTrace();
		}

		return buf;
	}

	/**
	 * Obté el nom d'un objecte
	 * 
	 * @param path
	 * @return
	 */
	public String ObtenirNom(String path) {
		String[] pathParts = path.split("/");

		return pathParts[pathParts.length - 1];
	}

	/**
	 * Obté el path del pare d'el objecte
	 * 
	 * @param path
	 * @return
	 */
	public String ObtenirDir(String path) {

		String[] pathParts = path.split("/");
		String pathnode;
		if (pathParts.length <= 2)
			pathnode = "/";
		else
			pathnode = ""; //

		for (int i = 1; i < pathParts.length - 1; i++) {
			pathnode = pathnode + "/" + pathParts[i];
		}

		return pathnode;
	}
/**
 * moure dades d'una ubicació a una altra.
 * 
 * 
 * @param from
 * @param to
 */
	public void moureEstructura(String from, String to) {
		String pareto;
		String nomto;

		pareto = this.ObtenirDir(to); // nom ruta desti
		nomto = this.ObtenirNom(to); // nom fitxer desti

		Estructura nodefrom;
		Directori nodeto;
		nodeto = (Directori) this.ObtenirNode(pareto); // directori desti
		nodefrom = this.ObtenirNode(from); // element a moure
		// canvi nom a element a moure

		nodefrom.getPare().EliminarFillRecursiu(nodefrom.getNom());
		p("nom del nodefrom abans del canvi " + nodefrom.getNom());
		nodefrom.setNom(nomto);
		p("nom del nodeto dp del canvi " + nodeto.getNom());
		nodeto.AfegirFill(nodefrom);
		this.DesaTree();

	}

}
